#include "stdafx.h"

RenderConsole* RenderConsole::mConsole = NULL;

RenderConsole::RenderConsole() : mD3d9(NULL)
	                            ,mDevice9(NULL)
							    ,mOffScreenSurface9(NULL)
								,mBackBufferSurface9(NULL)
							    ,mBackBuffer(NULL)
								,mClearColor(0)
								,mWidth(0) ,mHeight(0)
{

}

RenderConsole::~RenderConsole()
{

}

RenderConsole* RenderConsole::Instance()
{
	if (!mConsole)
	{
		mConsole = new RenderConsole();
	}
	return mConsole;
}

bool RenderConsole::InitializeConsole(int width, int height, HWND mainWindowHandler, bool windowed)
{
	mWidth = width;
	mHeight = height;
	if (!(mD3d9  =  Direct3DCreate9(D3D_SDK_VERSION)))   //create d3d point..
	{
		MessageBox(NULL, __T("Create D3D failed!"), __T("D3D Error"), MB_OK);
		return false;
	}

	D3DCAPS9 caps;

	mD3d9->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &caps);

	int vp = 0;      //indicate a hal mode or ref mode..
	if(caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
		vp = D3DCREATE_HARDWARE_VERTEXPROCESSING;   //Note ,in our pipleline, we calculate vertex processing all by ourselves. 
	else                                            //So it will be ok to choose either of them.Since we never use d3d pipleline.
		vp = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

	D3DPRESENT_PARAMETERS d3dpp;               //define d3d present parmeters..

	d3dpp.BackBufferWidth = mWidth;                           //set back buffer width..
	d3dpp.BackBufferHeight = mHeight;                         //set back buffer height..
	d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;                 //set back buffer format..
	d3dpp.BackBufferCount = 1;                                //we use only one back buffer..
	d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;              //d3d sample, we may used it cause we need d3d to do some parts of render..
	d3dpp.MultiSampleQuality = 0;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.hDeviceWindow = mainWindowHandler;
	d3dpp.Windowed = windowed;
	d3dpp.EnableAutoDepthStencil = true;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;             //set depth bit to 24,and stencil bit to 8..           
	d3dpp.Flags = 0;
	d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;     
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

	if (FAILED(mD3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, mainWindowHandler, vp, &d3dpp, &mDevice9))) //create d3d device..
	{
		mD3d9->Release(); //release d3d..
		MessageBox(NULL, __T("Create D3D device failed!"), __T("D3D Error"), MB_OK);
		return false;
	}

	mD3d9->Release();//release d3d...
	return true;
}

void RenderConsole::SwapBackBuffer()
{
	mDevice9->Present(NULL, NULL, NULL, NULL);
}

bool RenderConsole::LockBackBuffer()
{
	D3DLOCKED_RECT rec;
	if (FAILED(mDevice9->CreateOffscreenPlainSurface
		                (mWidth, mHeight, D3DFMT_A8R8G8B8,
						 D3DPOOL_DEFAULT, &mOffScreenSurface9, NULL)))
	{
		MessageBox(NULL, __T("Create D3D off-screen surface failed!"), __T("D3D Error"), MB_OK);
		return false;
	}

	mOffScreenSurface9->LockRect(&rec, NULL, D3DLOCK_DISCARD);
	mBackBuffer = (uint8*)rec.pBits;

	return true;
}

bool RenderConsole::UnlockBackBuffer()
{
	mOffScreenSurface9->UnlockRect();
	if (FAILED(mDevice9->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &mBackBufferSurface9)))
	{
		MessageBox(NULL, __T("Create D3D off-screen surface failed!"), __T("D3D Error"), MB_OK);
		return false;
	}

	mDevice9->StretchRect( mOffScreenSurface9, NULL, mBackBufferSurface9, NULL, D3DTEXF_NONE);

	//must relase back buffer..
	mBackBufferSurface9->Release();
	mOffScreenSurface9->Release();
	return true;
}

void RenderConsole::SetClearColor(int clearColor)
{
	mClearColor = clearColor;
}

void RenderConsole::ClearBackBuffer()
{
	for (int i = 0; i<mHeight*mWidth*4; i+=4)
	{
		mBackBuffer[i]    =   (mClearColor) & 0xff;      //blue..
		mBackBuffer[i+1]  =   (mClearColor>>8) & 0xff;   //grenn..
		mBackBuffer[i+2]  =   (mClearColor>>16) & 0xff;  //red..
		mBackBuffer[i+3]  =   (mClearColor>>24) & 0xff;  //alpha..
	}
}

void RenderConsole::SetPixelARGB(int x, int y, DWORD color)
{
	//caution to overflow.. we do not check it here...
	mBackBuffer[4 * (x + y*mWidth)]        =    (color) & 0xff;
	mBackBuffer[4 * (x + y*mWidth) + 1]    =    (color>>8) & 0xff;
	mBackBuffer[4 * (x + y*mWidth) + 2]    =    (color>>16) & 0xff;
	mBackBuffer[4 * (x + y*mWidth) + 3]    =    (color>>24) & 0xff;
}

void RenderConsole::BresenhamLine(int x0, int y0, int x1, int y1 , DWORD color)
{
	float k = (y1 - y0)/(float)(x1 - x0);
	float err = -0.5;

	for (; x0 <= x1; x0++)
	{
		SetPixelARGB(x0, y0, color);
		if (err >= 0)
		{
			y0++;
		}
		if (err >= 0)
		{
			err -= 1;
		}
		err += k;
	}
}